package com.koron.common.core.business.workflow.service;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.koron.bean.base.Response;
import com.koron.bean.query.Page;
import com.koron.bean.system.staff.vo.EamUser;
import com.koron.bean.workflow.*;
import com.koron.common.core.business.common.mapper.StaffMapper;
import com.koron.common.core.business.configsettings.utils.ConfigInfoTool;
import com.koron.common.core.business.workflow.bean.dto.WorkflowStaff;
import com.koron.common.core.business.workflow.bean.param.BaseParam;
import com.koron.common.core.business.workflow.bean.param.ListParam;
import com.koron.common.core.business.workflow.bean.param.StartParam;
import com.koron.common.core.business.workflow.bean.param.SubmitParam;
import com.koron.common.core.business.workflow.bean.po.ConfigureWorkflowPlugin;
import com.koron.common.core.business.workflow.bean.po.ConfigureWorkflowStaff;
import com.koron.common.core.business.workflow.bean.po.SysWorkflow;
import com.koron.common.core.business.workflow.bean.properties.WorkFlowConfigProperties;
import com.koron.common.core.business.workflow.bean.properties.WorkflowPlugin;
import com.koron.common.core.business.workflow.bean.properties.WorkflowProperties;
import com.koron.common.core.business.workflow.bean.result.DefineResult;
import com.koron.common.core.business.workflow.bean.result.FlowList;
import com.koron.common.core.business.workflow.bean.result.TaskStatistics;
import com.koron.common.core.business.workflow.bean.result.UserResult;
import com.koron.common.core.business.workflow.feign.ConfigureFeignService;
import com.koron.common.core.business.workflow.mapper.SysWorkflowMapper;
import com.koron.common.core.business.workflow.templates.SysWorkflowAware;
import com.koron.common.core.business.workflow.templates.WorkFlowHandler;
import com.koron.common.core.business.workflow.templates.WorkFlowTemplates;
import com.koron.common.core.business.workflow.util.TextUtil;
import com.koron.common.core.config.ThreadLocalContext;
import com.koron.common.core.util.CodeTools;
import com.koron.common.core.util.SpringUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.koron.ebs.mybatis.ADOConnection;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 工作流表(SysWorkflow)表服务实现类
 *
 * @author jzp
 * @since 2021-03-31 17:47:45
 */
@Service
@Slf4j
public class SysWorkflowService {

    @Autowired
    private WorkFlowHandler workFlowHandler;

    @Autowired
    private WorkFlowConfigProperties workFlowConfig;

    @Autowired
    private SysWorkflowAware sysWorkflowAware;

    @Value("${spring.profiles.active}")
    private String profile;

    public String getCurrentDbEnv() {
        return ThreadLocalContext.get().getCurrDs();
    }

    /**
     * 通过ID查询单条数据
     *
     * @param billId 主键
     * @return 实例对象
     */
    public SysWorkflow queryByBillId(String billId) {
        try (SessionFactory factory = new SessionFactory()) {
            factory.setDbEnv(getCurrentDbEnv());
            return factory.getMapper(SysWorkflowMapper.class).queryByBillId(billId);
        }
    }

    /**
     * 通过多个ID查询数据
     */
    public List<SysWorkflow> queryByBillIds(List<String> billIds) {
        try (SessionFactory factory = new SessionFactory()) {
            factory.setDbEnv(getCurrentDbEnv());
            return factory.getMapper(SysWorkflowMapper.class).queryByBillIds(billIds);
        }
    }

    public List<SysWorkflow> queryByFlowList(List<FlowList> flowList) {
        List<SysWorkflow> sysWorkflows = queryByBillIds(flowList.stream().map(FlowList::getBillId).collect(Collectors.toList()));
        Map<String, String> config = ConfigInfoTool.getConfigByCode("common", "upComing");
        // 永久关闭
        if (false && Objects.equals(config.get("autoSyncWorkflow"), "1")) {
            List<String> billIds = sysWorkflows.stream().map(SysWorkflow::getBillId).collect(Collectors.toList());

            /*
             * 保证工作流和业务数据一致，至少业务数据 > 工作流
             * 从billId来看，sysWorkflows是flowList的子集，所以要从flowList中删掉sysWorkflows
             */
            flowList.stream().filter(e -> !billIds.contains(e.getBillId())).forEach(flow -> {
                BaseParam baseParam = new BaseParam(flow.getAppCode(), flow.getSetCode(), flow.getProcessCode());
                WorkFlowTemplates.delete(baseParam, flow.getProcessInstanceId());
            });
        }
        return sysWorkflows;
    }

    /**
     * 通过多个ID查询数据
     */
    public List<String> queryDataIdsByBillIds(List<String> billIds) {
        List<SysWorkflow> sysWorkflows = queryByBillIds(billIds);
        return sysWorkflows.stream().map(SysWorkflow::getDataId).collect(Collectors.toList());
    }

    /**
     * 查询多条数据
     *
     * @return 对象列表
     */
    public List<SysWorkflow> queryAll(SysWorkflow sysWorkflow) {
        try (SessionFactory factory = new SessionFactory()) {
            factory.setDbEnv(getCurrentDbEnv());
            return factory.getMapper(SysWorkflowMapper.class).queryAll(sysWorkflow);
        }
    }


    /**
     * 根据DataId查询数据
     */
    public List<SysWorkflow> queryByDataId(String dataId) {
        if (StringUtils.isEmpty(dataId)) {
            return null;
        }
        SysWorkflow sysWorkflow = new SysWorkflow();
        sysWorkflow.setDataId(dataId);
        List<SysWorkflow> sysWorkflows = queryAll(sysWorkflow);
        if (sysWorkflows != null && sysWorkflows.size() != 0) {
            return sysWorkflows;
        }
        return null;
    }

    /**
     * 根据DataId查询
     * 只查询未结束的(正在流程中的)
     * 断言只有一条数据的IsFinished为1
     * 一条数据不能同时进入多个流程
     */
    public SysWorkflow queryRunByDataId(String dataId) {
        List<SysWorkflow> sysWorkflows = queryByDataId(dataId);
        if (sysWorkflows != null) {
            sysWorkflows.removeIf(e -> e.getIsFinished() == 1); // 移除已完成的
            if (sysWorkflows.size() != 0) {
                return sysWorkflows.get(0);
            }
        }
        return null;
    }

    /**
     * 根据DataId查询一条数据
     * 如果只有一条数据，直接返回
     * 如果有多条数据，只返回未结束的(正在流程中的)
     */
    public SysWorkflow queryOneByDataId(String dataId) {
        List<SysWorkflow> sysWorkflows = queryByDataId(dataId);
        if (sysWorkflows != null) {
            if (sysWorkflows.size() == 1) {
                return sysWorkflows.get(0);
            }
            sysWorkflows.removeIf(e -> e.getIsFinished() == 1); // 移除已完成的
            if (sysWorkflows.size() != 0) {
                return sysWorkflows.get(0);
            }
        }
        return null;
    }

    public String queryBillIdByDataId(String dataId) {
        List<SysWorkflow> sysWorkflows = queryByDataId(dataId);
        if (!CollectionUtils.isEmpty(sysWorkflows)) {
            SysWorkflow sysWorkflow = sysWorkflows.get(0);
            return sysWorkflow.getBillId();
        }
        return null;
    }

    /**
     * 条件查询多条数据
     *
     * @param page 条件分页查询对象
     * @return 条件对象列表
     */
    @TaskAnnotation("queryAllByLimit")
    public Page<SysWorkflow> queryAllByLimit(Page<SysWorkflow> page, SysWorkflow sysWorkflow) {
        try (SessionFactory factory = new SessionFactory()) {
            factory.setDbEnv(getCurrentDbEnv());
            page.setRecords(factory.getMapper(SysWorkflowMapper.class).queryAllByLimit(page, sysWorkflow));
            page.setTotal(factory.getMapper(SysWorkflowMapper.class).count(sysWorkflow));
            return page;
        }
    }

    /**
     * 根据id查询工作流界面html代码
     */
    public String queryWorkflowHTML(String id, String processCode, String variable) {
        // 不存在查则询工作流初始界面html代码
        if (StringUtils.isEmpty(id)) {
            return queryInitWorkflowHTML(processCode, variable);
        } else if (queryOneByDataId(id) == null) {
            if (StringUtils.isEmpty(processCode)) {
                return "工作流不存在";
            }
            return queryInitWorkflowHTML(processCode, variable);
        } else {
            // 如果存在工作流
            return workFlowHandler.queryView(id, variable) + getTag(getCurrNodeCode(id, null));
        }
    }

    /**
     * 根据id查询工作流初始界面html代码
     */
    public String queryInitWorkflowHTML(String processCode, String variable) {
        WorkflowProperties init = WorkflowProperties.init(processCode);
        return workFlowHandler.queryInitView(init.baseParam(), variable) + getTag(getCurrNodeCode(null, processCode));
    }

    public String queryInitOnlyWorkflowHTML(String processCode, String variable) {
        WorkflowProperties init = WorkflowProperties.init(processCode);
        return workFlowHandler.queryInitOnlyView(init.baseParam(), variable) + getTag(getCurrNodeCode(null, processCode));
    }

    /**
     * 根据id查询工作流界面只读html代码
     */
    public String queryOnlyWorkflowHTML(String id, String processCode, String variable) {
        // 不存在则查询工作流初始界面html代码
        if (StringUtils.isEmpty(id)) {
            return queryInitWorkflowHTML(processCode, variable);
        } else if (queryOneByDataId(id) == null) {
            if (StringUtils.isEmpty(processCode)) {
                return "工作流不存在";
            }
            return queryInitWorkflowHTML(processCode, variable);
        } else {
            // 如果存在工作流
            return workFlowHandler.queryOnlyView(id, variable) + getTag(getCurrNodeCode(id, null));
        }
    }

    /**
     * 根据id查询移动端工作流界面html代码
     */
    public String queryWorkflowMobileHTML(String id, String processCode) {
        // 不存在查则询工作流初始界面html代码
        if (StringUtils.isEmpty(id)) {
            return queryInitWorkflowMobileHTML(processCode);
        } else if (queryOneByDataId(id) == null) {
            if (StringUtils.isEmpty(processCode)) {
                return "工作流不存在";
            }
            return queryInitWorkflowMobileHTML(processCode);
        } else {
            // 如果存在工作流
            return workFlowHandler.queryViewMoBile(id) + getTag(getCurrNodeCode(id, null));
        }
    }

    /**
     * 根据id查询移动端工作流初始界面html代码
     */
    public String queryInitWorkflowMobileHTML(String processCode) {
        WorkflowProperties init = WorkflowProperties.init(processCode);
        return workFlowHandler.queryInitViewMoBile(init.baseParam()) + getTag(getCurrNodeCode(null, processCode));
    }

    /**
     * 根据id查询移动端工作流历史轨迹
     */
    public String queryWorkflowMobileHistory(String id) {
        if (StringUtils.isEmpty(id)) {
            return null;
        }
        return workFlowHandler.queryHistoryMobile(id);
    }

    public Page<FlowList> listBill(ListParam listParam) {
        String code = ThreadLocalContext.getApp().getCode();
        log.info("当前租户：{}", code);
        BaseParam baseParam = listParam.getBaseParam();
        // 查当前租户的流程
        if (baseParam == null) {
            baseParam = new BaseParam();
        }
        if (baseParam.getAppCode() == null) {
            baseParam.setAppCode(code);
        }
        listParam.setBaseParam(baseParam);
        return workFlowHandler.queryAll(listParam);
    }

    private String getTag(String content) {
        return "<currNodeCode hidden>" + content + "</currNodeCode>";
    }

    /**
     * 提交
     * service命名规则："${processCode}Service"，
     * 示例如： DefectInfoApproveService
     * 该service需要实现{@link ProcessCodeService}接口
     */
    public Response<Object> submit(EamUser user, SubmitQuery submitQuery) {
        /* -----提交前 ----- */
        SysWorkflow sysWorkflowBefore = queryRunByDataId(submitQuery.getDataId());

        /* ----- 提交后 ----- */
        SysWorkflow sysWorkflowAfter = null;
        if (sysWorkflowBefore == null) {
            // 为启动
            sysWorkflowAfter = activeStart(user, submitQuery);
            return redirect(sysWorkflowBefore, sysWorkflowAfter, submitQuery);
        } else {
            // 为提交
            // 如果有多个节点（并行节点），则需要多次提交（有这个权限）
            String[] taskIds = sysWorkflowBefore.getTaskId().split(";");
            if (taskIds.length <= 1) {
                // 单次提交
                sysWorkflowAfter = activeSubmit(user, submitQuery, sysWorkflowBefore, sysWorkflowBefore);
                return redirect(sysWorkflowBefore, sysWorkflowAfter, submitQuery);
            } else {
                // 多次提交
                // 只拿单个节点信息
                SysWorkflow sysWorkflowBeforeAlone = new SysWorkflow();
                BeanUtils.copyProperties(sysWorkflowBefore, sysWorkflowBeforeAlone);
                String[] curNodeUserIds = sysWorkflowBefore.getCurNodeUserId().split(";");
                String[] curNodeUserNames = sysWorkflowBefore.getCurNodeUserName().split(";");
                String[] curNodeNames = sysWorkflowBefore.getCurNodeName().split(";");
                String[] curNodeCodes = sysWorkflowBefore.getCurNodeCode().split(";");

                for (int i = 0; i < taskIds.length; i++) {
                    // 获取候选人
                    String curNodeUserId = curNodeUserIds[i];
                    // 只有在候选人队列里面的才能提交
                    if (!curNodeUserId.contains(user.getAccount())) {
                        continue;
                    }
                    sysWorkflowBeforeAlone.setTaskId(taskIds[i]);
                    sysWorkflowBeforeAlone.setCurNodeUserId(curNodeUserIds[i]);
                    sysWorkflowBeforeAlone.setCurNodeUserName(curNodeUserNames[i]);
                    sysWorkflowBeforeAlone.setCurNodeName(curNodeNames[i]);
                    sysWorkflowBeforeAlone.setCurNodeCode(curNodeCodes[i]);
                    sysWorkflowAfter = activeSubmit(user, submitQuery, sysWorkflowBeforeAlone, sysWorkflowBefore);
                    return redirect(sysWorkflowBeforeAlone, sysWorkflowAfter, submitQuery);

                }
            }

        }

        return Response.ok();
    }

    public SysWorkflow activeStart(EamUser user, SubmitQuery submitQuery) {
        WorkflowProperties prop = WorkflowProperties.init(submitQuery.getProcessCode());
        StartParam startParam = new StartParam(prop.baseParam());
        BeanUtils.copyProperties(submitQuery, startParam);
        startParam.setFormVariables(TextUtil.toMapFromString(submitQuery.getFormVariables()));
        startParam.setOperator(user.getAccount());
        return workFlowHandler.start(startParam, submitQuery.getDataId(), submitQuery.getDataCode(), submitQuery.getDataTitle(), prop);
    }

    public SysWorkflow activeSubmit(EamUser user, SubmitQuery submitQuery, SysWorkflow sysWorkflowBeforeAlone, SysWorkflow sysWorkflowBefore) {
        if (sysWorkflowBeforeAlone == null) {
            return null;
        }

        // 两票不允许驳回后返回本节点
        if (Objects.equals(submitQuery.getRejectType(), 1) && Objects.equals(submitQuery.getSetCode(), "ticket")) {
            throw new IllegalArgumentException("涉及到两票业务，不允许驳回后返回本节点");
        }

        // 不允许沟通或转办给流程处理人
        if ((submitQuery.getOperation() == 2 || submitQuery.getOperation() == 4) && sysWorkflowBeforeAlone.getCurNodeUserId().contains(submitQuery.getRelatedUser())) {
            if (submitQuery.getOperation() == 2) {
                throw new IllegalArgumentException("不允许转办给流程处理人");
            }
            if (submitQuery.getOperation() == 4) {
                throw new IllegalArgumentException("不允许沟通流程处理人");
            }
        }

        // 默认从前端拿到taskId，如果没有，再从数据库拿
        String taskIdBefore = submitQuery.getTaskId(); // 前端taskId
        String taskIdAfter = sysWorkflowBeforeAlone.getOneTaskId(); // 后端taskId
        // 断言前后端拿到的taskId是一致的
        Assert.isTrue(Objects.equals(taskIdBefore, taskIdAfter) || "$^__^$".equals(taskIdAfter), "数据状态有变化，请刷新页面后重试");

        SubmitParam submitParam = new SubmitParam()
                .baseParam(sysWorkflowBeforeAlone.drawBaseParam())
                .billId(sysWorkflowBeforeAlone.getBillId())
                .taskId(taskIdBefore)
                .operator(user.getAccount())
                .formVariables(submitQuery.getFormVariables());

        BeanUtils.copyProperties(submitQuery, submitParam);

        // 处理一下移动端只有节点id的问题，此时可以将rejectNodeId转为RejectNodeCode
        if (submitQuery.getOperation() == 1 && submitQuery.getRejectNodeCode() == null && submitQuery.getRejectNodeId() != null) {
            String rejectNodeCode = workFlowHandler.getCodeById(sysWorkflowBeforeAlone.drawBaseParam(), submitQuery.getRejectNodeId());
            submitParam.setRejectNodeCode(rejectNodeCode);
        }

        return workFlowHandler.submit(submitParam, sysWorkflowBefore, submitQuery.getDataCode(), submitQuery.getDataTitle());
    }

    private Response<Object> redirect(SysWorkflow sysWorkflowBefore, SysWorkflow sysWorkflowAfter, SubmitQuery submitQuery) {
        // 每次提交都需要通知待办
        Assert.notNull(sysWorkflowAfter, "提交失败，提交后数据为空");
        sysWorkflowAware.sendUsersToOa(sysWorkflowBefore, sysWorkflowAfter);
        // 每次提交都需要发送短信或邮件

        String dataTitle = sysWorkflowBefore == null ?
                (StringUtils.isEmpty(sysWorkflowAfter.getTitle()) ?
                        (StringUtils.isEmpty(submitQuery.getDataTitle()) ?
                                "" : submitQuery.getDataTitle()) : sysWorkflowAfter.getTitle()) :
                sysWorkflowBefore.getTitle();
        // 结束节点不通知
        if (!Arrays.stream(sysWorkflowAfter.getCurNodeCode().split(";")).allMatch("END"::equals)) {
            SysWorkflow currWorkflow = sysWorkflowBefore == null ? sysWorkflowAfter : sysWorkflowBefore;
            String curNodeCode = sysWorkflowAfter.getCurNodeCode();
            log.info("SysWorkflowService:redirect:curNodeCode:{}",curNodeCode);
            // 暂时先不处理作废通知和并行节点
            if (!(submitQuery.getOperation() == 16 || curNodeCode.contains(";"))) {
                List<DefineResult> define = workFlowHandler.define(currWorkflow.drawBaseParam(), null);
                DefineResult defineResult = define.stream().filter(e -> sysWorkflowAfter.getCurNodeCode().equals(e.getNodeCode())).findFirst().orElseThrow(() -> new IllegalArgumentException("找不到当前节点：" + sysWorkflowAfter.getCurNodeName()));
                log.info("SysWorkflowService:redirect:defineResult:{}",defineResult);
                /*sysWorkflowAware.sendMsg(defineResult.getNoticeWay(),
                        submitQuery.getOperation(),
                        sysWorkflowBefore == null ? ThreadLocalContext.get().getName() : sysWorkflowBefore.getCurNodeUserName(),
                        sysWorkflowAfter.getCurNodeUserName(),
                        dataTitle,
                        sysWorkflowAfter.getProcessCode(),
                        sysWorkflowAfter.getDataId(),
                        sysWorkflowAfter.getCurNodeUserId());*/
                sysWorkflowAware.sendMsg(sysWorkflowBefore, sysWorkflowAfter, defineResult, submitQuery);
            }
        }

        /*
         * 如果提交后当前节点编码为CANCEL或END,则需要更新业务状态
         * 如果提交后当前节点编码以call-开头,则需要更新业务状态
         * 如果提交前当前节点编码以draft-开头,则需要更新业务状态
         * 如果是驳回操作
         *  驳回到草稿（即驳回后节点编码以draft-开头） ,则需要更新业务状态
         *  驳回前当前节点编码以call-开头,则需要更新业务状态
         * 如果是撤回操作,则需要更新业务状态
         * 如果是废弃，则需要更新业务状态
         */
        if (
                (
                        submitQuery.getOperation() == 32 &&  // 如果是通过操作
                                (
                                        (StrUtil.contains(sysWorkflowAfter.getCurNodeCode(), "CANCEL")) || // CANCEL
                                                (Arrays.stream(sysWorkflowAfter.getCurNodeCode().split(";")).allMatch("END"::equals)) || // END
                                                sysWorkflowAfter.getCurNodeCode().startsWith(workFlowConfig.getCall()) || // 提交后当前节点编码以call-开头
                                                sysWorkflowBefore.getCurNodeCode().startsWith(workFlowConfig.getDraft()) // 提交前当前节点编码以draft-开头。如果是启动流程，应该走不到这一步，因为下个节点以call开头，所以不处理空指针（必须保证流程中有draft-和call-节点）
                                )
                ) ||
                        (
                                submitQuery.getOperation() == 1 &&  // 如果是驳回操作
                                        (
                                                sysWorkflowAfter.getCurNodeCode().startsWith(workFlowConfig.getDraft()) || // 驳回到草稿 ,则需要更新业务状态
                                                        Objects.requireNonNull(sysWorkflowBefore).getCurNodeCode().startsWith(workFlowConfig.getCall())     // 驳回前当前节点编码以call-开头,则需要更新业务状态
                                        )
                        ) || submitQuery.getOperation() == 16 || submitQuery.getOperation() == 256
        ) {
            // 回调业务模块
            WorkflowProperties workflowProperties = WorkflowProperties.init(sysWorkflowAfter.getProcessCode());
            String callbackPrefix = workflowProperties.getCallbackController();
            return sysWorkflowAware.redirectUpdateState(callbackPrefix, sysWorkflowAfter, submitQuery);
        }
        return null;
    }

    /**
     * 新增数据
     *
     * @param sysWorkflow 实例对象
     * @return 实例对象
     */
    public SysWorkflow insert(SysWorkflow sysWorkflow) {
        sysWorkflow.setId(CodeTools.getCode32());
        try (SessionFactory factory = new SessionFactory()) {
            factory.setDbEnv(getCurrentDbEnv());
            if (sysWorkflow.getBillId() == null) {
                sysWorkflow.setBillId(CodeTools.getCode32());
            }
            sysWorkflow.setIsFinished(0);
            factory.getMapper(SysWorkflowMapper.class).insert(sysWorkflow);
        }
        return this.queryByBillId(sysWorkflow.getBillId());
    }

    /**
     * 修改数据
     *
     * @param sysWorkflow 实例对象
     * @return 实例对象
     */
    public SysWorkflow update(SysWorkflow sysWorkflow) {
        return ADOConnection.runTask(getCurrentDbEnv(), factory-> {
            factory.getMapper(SysWorkflowMapper.class).update(sysWorkflow);
            return this.queryByBillId(sysWorkflow.getBillId());
        }, SysWorkflow.class);
    }

    /**
     * 完成工作流
     *
     * @param billId 主键
     * @return 是否成功
     */
    public boolean finishById(String billId) {
        try (SessionFactory factory = new SessionFactory()) {
            factory.setDbEnv(getCurrentDbEnv());
            return factory.getMapper(SysWorkflowMapper.class).finished(billId) > 0;
        }
    }

    /**
     * 通过主键删除数据
     *
     * @param billId 主键
     * @return 是否成功
     */
    public boolean deleteByBillId(String billId) {
        try (SessionFactory factory = new SessionFactory()) {
            factory.setDbEnv(getCurrentDbEnv());
            return factory.getMapper(SysWorkflowMapper.class).deleteByBillId(billId) > 0;
        }
    }

    /**
     * 通过主键删除数据
     *
     * @param dataId 主键
     * @return 是否成功
     */
    public boolean deleteByDataId(String dataId) {
        try (SessionFactory factory = new SessionFactory()) {
            factory.setDbEnv(getCurrentDbEnv());
            return factory.getMapper(SysWorkflowMapper.class).deleteByDataId(dataId) > 0;
        }
    }

    /**
     * 获取运行中的节点处理人信息
     */
    public List<UserResult> runTaskCandidateInfo(String dataId) {
        SysWorkflow workflow = queryRunByDataId(dataId);
        BaseParam baseParam = workflow.drawBaseParam();
        String processInstanceId = workflow.getProcessInstanceId();
        return workFlowHandler.runTaskCandidateInfo(baseParam, processInstanceId);
    }

    /**
     * 获取人员待办任务数量汇总
     */
    public List<TaskStatistics> listUserTaskStatistics(String startDate, String endDate, String setCode, List<String> accountList) {
        // 获取当前租户编码
        String code = ThreadLocalContext.getApp().getCode();
        return workFlowHandler.listUserTaskStatistics(new BaseParam(code, setCode, ""), startDate, endDate, accountList);
    }

    /**
     * 获取人员待办任务数量汇总
     */
    public List<TaskStatistics> listUserTaskStatistics(String startDate, String endDate, List<String> accountList) {
        return listUserTaskStatistics(startDate,endDate,"",accountList);
    }

    /**
     * 获取处理人
     * 给自动选人组件使用
     *
     * @return 处理人为空时，前端会直接显示默认选人组件
     */
    public List<WorkflowStaff> getHandler(String processCode, String dataNode, String formVariables) {
        // 1. 根据dataNode找到nodeCode
        WorkflowProperties prop = WorkflowProperties.init(processCode);
        String nodeCode = workFlowHandler.getCodeById(prop.baseParam(), dataNode);

        // 2. 根据processCode和nodeCode找到对应插件url
        ConfigureFeignService configureFeignService = SpringUtils.getBean(ConfigureFeignService.class);
        Response<WorkflowPlugin> response = configureFeignService.getPluginUrlByNode(processCode, nodeCode, 0);
        return getUserByPlugin(response.getData(), formVariables, "/" + WorkflowPluginController.handler);
    }

    /**
     * 提交人
     * 给节点出入口使用
     *
     * @return 返回的人员不能为空
     */
    public List<WorkflowStaff> getSubmitter(String processCode, String nodeCode, String nextNodeCode, String formVariables) {
        // 1. 根据processCode和nodeCode找到对应插件
        // 如果有nextNodeCode，一般为人工决策，需要指定为入口规则2; 否则为正常分支，type为1
        int type = 1;
        if (!StringUtils.isEmpty(nextNodeCode)) {
            type = 2;
            // 根据nextNodeCode找到nodeCode
            WorkflowProperties prop = WorkflowProperties.init(processCode);
            nodeCode = workFlowHandler.getCodeById(prop.baseParam(), nextNodeCode);
            // 如果目标节点是结束节点，查出来的nodeCode则是null
            if (nodeCode == null) {
                return null;
            }
        }
        ConfigureFeignService configureFeignService = SpringUtils.getBean(ConfigureFeignService.class);
        Response<WorkflowPlugin> response = configureFeignService.getPluginUrlByNode(processCode, nodeCode, type);
        List<WorkflowStaff> userByPlugin = getUserByPlugin(response.getData(), formVariables, "/" + WorkflowPluginController.submitter);
        // 如果返回的是[]，则报错
        if (userByPlugin != null && userByPlugin.size() == 0) {
            throw new IllegalArgumentException("未找到下一个节点的审批人，请检查工作流插件配置人员信息");
        }
        return userByPlugin;
    }

    private List<WorkflowStaff> getUserByPlugin(WorkflowPlugin workflowPlugin, String formVariables, String postUrl) {
        ConfigureWorkflowPlugin configureWorkflowPlugin;
        // 如果有这个插件，且有accessUrl
        if (workflowPlugin != null &&
                (configureWorkflowPlugin = workflowPlugin.getConfigureWorkflowPlugin()) != null &&
                StringUtils.isNotEmpty(configureWorkflowPlugin.getAccessUrl())
        ) {
            // 转换人员列表
            List<WorkflowUser> users = new ArrayList<>();
            Optional.ofNullable(workflowPlugin.getConfigureWorkflowStaffList()).ifPresent(staffs -> {
                for (ConfigureWorkflowStaff configureWorkflowStaff : staffs) {
                    WorkflowUser workflowUser = new WorkflowUser();
                    workflowUser.setName(configureWorkflowStaff.getName());
                    workflowUser.setCode(configureWorkflowStaff.getCode());
                    workflowUser.setType(configureWorkflowStaff.getType());
                    users.add(workflowUser);
                }
            });
            // 构造url参数
            SubmitterParam submitterParam = new SubmitterParam();
            submitterParam.setUsers(users);
            submitterParam.setFormVariables(formVariables);
            if (Objects.nonNull(configureWorkflowPlugin.getUserConfig())){
                String userConfig = configureWorkflowPlugin.getUserConfig();
                List<WorkflowCustomUser> customUserList = JSONArray.parseArray(userConfig, WorkflowCustomUser.class);
                submitterParam.setCustomUsers(customUserList);
            }
            // 2. 调用插件接口，获取用户信息
            Response<List<WorkflowUser>> resp = sysWorkflowAware.redirectListWorkflowUser(fixUrl(configureWorkflowPlugin.getAccessUrl(), postUrl), submitterParam);

            List<WorkflowUser> workflowUsers = resp.getData();
            // 如果是null，则返回null，会弹出选所有人的组件
            if (workflowUsers == null) {
                return null;
            }

            Map<String, String> accountMap = workflowUsers.stream().collect(Collectors.toMap(WorkflowUser::getCode, WorkflowUser::getName, (v1, v2) -> v1));
            Map<String, String> config = ConfigInfoTool.getConfigByCode("common", "workflow");
            String enableDefaultAccount = config.get("enableDefaultAccount");
            // 添加默认测试用户
            if ("1".equals(enableDefaultAccount) && StringUtils.equalsAny(profile, "dev", "test")) {
                accountMap.putIfAbsent("test", "测试");
            }
            // 如果是空，则确实是空数据
            if (CollectionUtils.isEmpty(accountMap)) {
                return new ArrayList<>();
            }
            List<WorkflowStaff> workflowStaffs;
            try (SessionFactory factory = new SessionFactory()) {
                StaffMapper mapper = factory.getMapper(StaffMapper.class);
                workflowStaffs = mapper.selectStaffOrg(new ArrayList<>(accountMap.keySet()));
                for (WorkflowStaff workflowStaff : workflowStaffs) {
                    workflowStaff.setName(accountMap.get(workflowStaff.getAccount()));
                }
            }
            return workflowStaffs;
        }
        return null;
    }

    /**
     * 拼接accessUrl+postUrl
     * 如果accessUrl以/开头，则不拼接
     * @param accessUrl 插件接口url，如eam-service-configure/pluginWorkflow/oneToOne
     * @param postUrl 后缀，如submitter
     * @return 相对url
     */
    @SneakyThrows
    private String fixUrl(String accessUrl, String postUrl) {
        if (accessUrl.startsWith("/")) {
            // 如果包含?，则将问号后面的数据都编码
            if (accessUrl.contains("?")) {
                int index = accessUrl.indexOf("?");
                String postfix = accessUrl.substring(index + 1);
                postfix = postfix.replaceAll(">", URLEncoder.encode(">", "UTF-8"));
                postfix = postfix.replaceAll("<", URLEncoder.encode("<", "UTF-8"));
                return accessUrl.substring(1, index + 1) + postfix;
            }
            return accessUrl.substring(1);
        }
        return accessUrl + postUrl;
    }

    /**
     * 获取当前节点编号
     */
    private String getCurrNodeCode(String dataId, String processCode) {
        if (!StringUtils.isEmpty(dataId)) {
            SysWorkflow workflow = queryRunByDataId(dataId);
            if (workflow != null) {
                String curNodeCode = workflow.getCurNodeCode();
                // 处理并行节点
                String[] curNodeCodes = curNodeCode.split(";");
                // 如果没有并行节点，直接返回
                if (curNodeCodes.length <= 1) {
                    return curNodeCode;
                }
                // 获取当前用户所在节点位置
                String curNodeUserId = workflow.getCurNodeUserId();
                String[] curNodeUserIds = curNodeUserId.split(";");
                for (int i = 0; i < curNodeUserIds.length; i++) {
                    // 如果当前登陆人在这个节点中
                    if (curNodeUserIds[i].contains(ThreadLocalContext.get().getAccount())) {
                        return curNodeCodes[i];
                    }
                }
                // 应该不会执行到这里的
                return "";
            }
            // 流程已结束，当作流程未开始来处理
            // 这里应该显示结束节点，但有些业务会有两个流程，所以显示草稿节点
            workflow = queryOneByDataId(dataId);
            if (workflow != null) {
                processCode = workflow.getProcessCode();
            }
        }
        // 流程未开始，返回起草节点
        if (!StringUtils.isEmpty(processCode)) {
            WorkflowProperties prop = WorkflowProperties.init(processCode);
            // 获取工作流节点信息
            List<DefineResult> define = workFlowHandler.define(prop.baseParam(), null);
            Assert.notEmpty(define, "该流程节点信息为空");
            // 以draft-开头的为起草节点，否则拿第一个节点
            return define.stream().filter(e -> e.getNodeCode().startsWith("draft-")).findFirst().orElse(define.get(0)).getNodeCode();
        }
        throw new IllegalArgumentException("dataId和processCode不能同时为空");
    }

}
